/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "geofence_napi.h"

#include "geofence_common.h"

using namespace OHOS::Msdp;
using namespace OHOS;
namespace {
auto &g_GeofenceClient = GeofenceClient::GetInstance();

static napi_ref g_responseConstructor;
static const uint8_t ARG_0 = 0;
static const uint8_t ARG_1 = 1;
static const uint8_t ARG_2 = 2;
static const uint8_t ARG_3 = 3;
static const int32_t BUF_LEN = 128;
static const int32_t CALLBACK_SUCCESS = 200;
static const int32_t ERROR_MESSAGE = -1;
static const std::vector<std::string> vecGeofenceValue {
    "VALUE_OUTSIDE", "VALUE_INSIDE","VALUE_ENTER", "VALUE_EXIT"
};
}
std::map<int32_t, sptr<IgeofenceCallback>> GeofenceNapi::callbackMap_;
std::map<int32_t, GeofenceNapi*> GeofenceNapi::objectMap_;
napi_ref GeofenceNapi::geofenceValueRef_;

struct ResponseEntity {
    GeofenceDataUtils::GeofenceValue value;
};

void GeofenceCallback::OnGeofenceChanged(const GeofenceDataUtils::GeofenceData& geofenceData)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Callback enter");
    GeofenceNapi *geofenceNapi = GeofenceNapi::GetGeofenceNapi(geofenceData.type);
    if (geofenceNapi == nullptr) {
        return;
    }
    geofenceNapi->OnGeofenceChangedDone(static_cast<int32_t> (geofenceData.type),
        static_cast<int32_t> (geofenceData.callbackvalue), false);
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Callback exit");
}

GeofenceNapi *GeofenceNapi::GetGeofenceNapi(int32_t type)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Enter, type = %{public}d", type);
    GeofenceNapi* obj = nullptr;
    bool isExists = false;
    for (auto it = objectMap_.begin(); it != objectMap_.end(); ++it) {
        if (it->first == type) {
            isExists = true;
            obj = (GeofenceNapi*)(it->second);
            GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Found object");
            break;
        }
    }
    if (!isExists) {
        GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Didn't find object");
    }
    return obj;
}

GeofenceNapi::GeofenceNapi(napi_env env, napi_value thisVar) : GeofenceEvent(env, thisVar)
{
    env_ = env;
    callbackRef_ = nullptr;
    geofenceValueRef_ = nullptr;
}

GeofenceNapi::~GeofenceNapi()
{
    if (callbackRef_ != nullptr) {
        napi_delete_reference(env_, callbackRef_);
    }

    if (geofenceValueRef_ != nullptr) {
        napi_delete_reference(env_, geofenceValueRef_);
    }
}

void GeofenceNapi::OnGeofenceChangedDone(const int32_t& type, const int32_t& value, bool isOnce)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Enter, value = %{public}d", value);
    OnEvent(type, ARG_1, value, isOnce);
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Exit");
}
bool GeofenceNapi::HasNamedProperty(napi_env env, napi_value object, const char *propertyName)
{
    bool hasProperty = false;
    napi_has_named_property(env, object, propertyName, &hasProperty);
    return hasProperty;
}

napi_value GeofenceNapi::GetNamedProperty(napi_env env, napi_value object, const char *propertyName)
{
    napi_value value = nullptr;
    napi_get_named_property(env, object, propertyName, &value);
    return value;
}

void GeofenceNapi::InvokeCallBack(napi_env env, napi_value *args, bool voidParameter, int32_t value)
{
    napi_value callback = nullptr;
    napi_value indexObj = nullptr;
    napi_create_object(env, &indexObj);
    napi_value successIndex = nullptr;
    if (!voidParameter){
        napi_create_int32(env, value, &successIndex);
    }

    napi_ref callbackSuccess = nullptr;
    napi_value ret;
    napi_set_named_property(env, indexObj, "geofenceValue", successIndex);
    napi_create_reference(env, args[ARG_2], 1, &callbackSuccess);
    napi_get_reference_value(env, callbackSuccess, &callback);
    napi_call_function(env, nullptr, callback, 1, &indexObj, &ret);
    napi_delete_reference(env, callbackSuccess);
}

napi_value GeofenceNapi::SubscribePointGeofence(napi_env env, napi_callback_info info)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Enter");
    napi_value result = nullptr;
    size_t argc = ARG_3;
    napi_value args[ARG_3] = {0};
    napi_value jsthis;
    void *data = nullptr;
    GeofenceDataUtils::PointOptions pointOptions;


    napi_status status = napi_get_cb_info(env, info, &argc, args, &jsthis, &data);
    NAPI_ASSERT(env, (status == napi_ok) && (argc >= 0), "Bad parameters");

    //type
    napi_valuetype valueType1 = napi_undefined;
    napi_typeof(env, args[ARG_0], &valueType1);
    NAPI_ASSERT(env, valueType1 == napi_number, "type mismatch for parameter 1");

    //options
    napi_valuetype valueType2 = napi_undefined;
    napi_typeof(env, args[ARG_1], &valueType2);
    NAPI_ASSERT(env, valueType2 == napi_object, "type mismatch for parameter 2");

    //callback
    napi_valuetype valueType3 = napi_undefined;
    napi_typeof(env, args[ARG_2], &valueType3);
    NAPI_ASSERT(env, valueType3 == napi_function, "type mismatch for parameter 3");

    //type    get value
    int32_t type;
    status = napi_get_value_int32(env, args[ARG_0], &type);
    if (status != napi_ok) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Failed to get type");
        return result;
    }

    if (type != GeofenceDataUtils::GeofenceType::GEOFENCE_OPTION_ADD_POINT_GEOFENCE) {
        InvokeCallBack(env, args, false, ERROR_MESSAGE);
        return result;
    }

    //options    get value
    const char *centerCoordinateStr = "centerCoordinate";
    const char *radiusStr = "radius";
    bool hascenterCoordinate = HasNamedProperty(env, args[ARG_1], centerCoordinateStr);
    bool hasradius = HasNamedProperty(env, args[ARG_1], radiusStr);
    NAPI_ASSERT(env, hascenterCoordinate && hasradius, "type mismatch");

    //options    arr
    bool arrayResult = false;
    napi_value centerCoordinateValue = nullptr;
    centerCoordinateValue = GetNamedProperty(env, args[ARG_1], centerCoordinateStr);
     status =  napi_is_array(env, centerCoordinateValue  , &arrayResult);
    if (status != napi_ok) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Failed to napi_is_array");
        return result;
    }
    else {
        GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "arrayResult = %{public}d", arrayResult);
    }

    uint32_t arrayLengthResult  = 0;
    status = napi_get_array_length(env, centerCoordinateValue, &arrayLengthResult);

    GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "arrayLengthResult: %{public}d", arrayLengthResult);

    for (int arrayLen = 0; arrayLen < arrayLengthResult; arrayLen++) {
        napi_value arrayValue;
        status = napi_get_element(env, centerCoordinateValue, arrayLen, &arrayValue);
        if (status != napi_ok) {
            return result;
        }
        double CenterCoordinate;
        napi_get_value_double(env, arrayValue, &CenterCoordinate);
        GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "CenterCoordinate %{public}d = %{public}lf", arrayLen, CenterCoordinate);
        pointOptions.centerCoordinate.push_back(CenterCoordinate);
    }

    //Options    radius
    napi_value radiusValue = nullptr;
    radiusValue = GetNamedProperty(env, args[ARG_1], radiusStr);
    double radius;
    napi_get_value_double(env, radiusValue, &radius);
    pointOptions.radius = radius;
    GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "radius = %{public}lf", pointOptions.radius);

    GeofenceNapi* obj = nullptr;
    bool isObjExists = false;
    for (auto it = objectMap_.begin(); it != objectMap_.end(); ++it) {
        if (it->first == type) {
            isObjExists = true;
            GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Object already exists");
            return result;
        }
    }
    if (!isObjExists) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Didn't find object, so created it");
        obj = new GeofenceNapi(env, jsthis);
        napi_wrap(env, jsthis, reinterpret_cast<void *>(obj),
            [](napi_env env, void *data, void *hint) {
                (void)env;
                (void)hint;
                GeofenceNapi *geofence = (GeofenceNapi *)data;
                delete geofence;
            },
            nullptr, &(obj->callbackRef_));
        objectMap_.insert(std::pair<int32_t, GeofenceNapi*>(type, obj));
    }

    if (!obj->On(type, args[ARG_2], false)) {
            GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "type: %{public}d already exists", type);
            return result;
    }

    sptr<IgeofenceCallback> callback;
    bool isCallbackExists = false;
    for (auto it = callbackMap_.begin(); it != callbackMap_.end(); ++it) {
        if (it->first == type) {
            isCallbackExists = true;
            break;
        }
    }
    if (!isCallbackExists) {
        callback = new GeofenceCallback();
        GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Didn't find callback, created it: %{public}p", callback.GetRefPtr());

        g_GeofenceClient.SubscribePoint(GeofenceDataUtils::GeofenceType(type), pointOptions, callback);
        callbackMap_.insert(std::pair<int32_t, sptr<IgeofenceCallback>>(type, callback));
        InvokeCallBack(env, args, false, CALLBACK_SUCCESS);
    } else {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Callback exists.");
        return result;
    }

    napi_get_undefined(env, &result);
    GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Exit");
    return result;
}

napi_value GeofenceNapi::UnSubscribePointGeofence(napi_env env, napi_callback_info info)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Enter");
    napi_value result = nullptr;
    size_t argc = ARG_3;
    napi_value args[ARG_3] = { 0 };
    napi_value jsthis;
    void *data = nullptr;
    GeofenceDataUtils::PointOptions pointOptions;

    napi_status status = napi_get_cb_info(env, info, &argc, args, &jsthis, &data);
    NAPI_ASSERT(env, (status == napi_ok) && (argc >= 0), "Bad parameters");

    napi_valuetype valueType1 = napi_undefined;
    napi_typeof(env, args[ARG_0], &valueType1);
    GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "valueType1: %{public}d", valueType1);
    NAPI_ASSERT(env, valueType1 == napi_number, "type mismatch for parameter 1");

    napi_valuetype valueType2 = napi_undefined;
    napi_typeof(env, args[ARG_1], &valueType2);
    NAPI_ASSERT(env, valueType2 == napi_object, "type mismatch for parameter 2");

    int32_t type;
    status = napi_get_value_int32(env, args[ARG_0], &type);
    if (status != napi_ok) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Failed to get type");
        return result;
    }

    if (type != GeofenceDataUtils::GeofenceType::GEOFENCE_OPTION_ADD_POINT_GEOFENCE) {
        InvokeCallBack(env, args, true, ERROR_MESSAGE);
        return result;
    }

    //options    get value
    const char *centerCoordinateStr = "centerCoordinate";
    const char *radiusStr = "radius";
    bool hascenterCoordinate = HasNamedProperty(env, args[ARG_1], centerCoordinateStr);
    bool hasradius = HasNamedProperty(env, args[ARG_1], radiusStr);
    NAPI_ASSERT(env, hascenterCoordinate && hasradius, "type mismatch");

    //options    arr
    bool arrayResult = false;
    napi_value centerCoordinateValue = nullptr;
    centerCoordinateValue = GetNamedProperty(env, args[ARG_1], centerCoordinateStr);
     status =  napi_is_array(env, centerCoordinateValue  , &arrayResult);
    if (status != napi_ok) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Failed to napi_is_array");
        return result;
    }
    else {
        GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "arrayResult = %{public}d", arrayResult);
    }

    uint32_t arrayLengthResult  = 0;
    status = napi_get_array_length(env, centerCoordinateValue, &arrayLengthResult);

    GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "arrayLengthResult: %{public}d", arrayLengthResult);

    for (int arrayLen = 0; arrayLen < arrayLengthResult; arrayLen++) {
        napi_value arrayValue;
        status = napi_get_element(env, centerCoordinateValue, 0, &arrayValue);
        if (status != napi_ok) {
            return result;
        }
        double CenterCoordinate;
        napi_get_value_double(env, arrayValue, &CenterCoordinate);
        GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "CenterCoordinate = %{public}lf", CenterCoordinate);
        pointOptions.centerCoordinate.push_back(CenterCoordinate);
    }

    //Options    radius
    napi_value radiusValue = nullptr;
    radiusValue = GetNamedProperty(env, args[ARG_1], radiusStr);
    double radius;
    napi_get_value_double(env, radiusValue, &radius);
    pointOptions.radius = radius;
    GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "radius = %{public}lf", pointOptions.radius);

    GeofenceNapi* obj = nullptr;
    bool isObjExists = false;
    for (auto it = objectMap_.begin(); it != objectMap_.end(); ++it) {
        if (it->first == type) {
            isObjExists = true;
            obj = (GeofenceNapi*)(it->second);
            GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Found object");
        }
    }
    if (!isObjExists) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Didn't find object, so created it");
        return result;
    }

    if (!obj->Off(type, false)) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Failed to get callback for type: %{public}d", type);
        return result;
    } else {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "erase ObjMap_");
        objectMap_.erase(type);
    }

    sptr<IgeofenceCallback> callback;
    bool isCallbackExists = false;
    for (auto it = callbackMap_.begin(); it != callbackMap_.end(); ++it) {
        if (it->first == type) {
            isCallbackExists = true;
            callback = (sptr<IgeofenceCallback>)(it->second);
            GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Found callback: %{public}p", callback.GetRefPtr());
            break;
        }
    }
    if (!isCallbackExists) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "No existed callback");
        return result;
    } else if (callback != nullptr) {
        g_GeofenceClient.UnSubscribePoint(GeofenceDataUtils::GeofenceType(type), pointOptions, callback);

        InvokeCallBack(env, args, true, CALLBACK_SUCCESS);
        callbackMap_.erase(type);
    }
    napi_get_undefined(env, &result);
    GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Exit");
    return result;
}

napi_value GeofenceNapi::SubscribePoiGeofence(napi_env env, napi_callback_info info)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Enter");
    napi_value result = nullptr;
    size_t argc = ARG_3;
    napi_value args[ARG_3] = {0};
    napi_value jsthis;
    void *data = nullptr;


    napi_status status = napi_get_cb_info(env, info, &argc, args, &jsthis, &data);
    NAPI_ASSERT(env, (status == napi_ok) && (argc >= 0), "Bad parameters");

    //type
    napi_valuetype valueType1 = napi_undefined;
    napi_typeof(env, args[ARG_0], &valueType1);
    NAPI_ASSERT(env, valueType1 == napi_number, "type mismatch for parameter 1");

    //options
    napi_valuetype valueType2 = napi_undefined;
    napi_typeof(env, args[ARG_1], &valueType2);
    NAPI_ASSERT(env, valueType2 == napi_object, "type mismatch for parameter 2");

    //callback
    napi_valuetype valueType3 = napi_undefined;
    napi_typeof(env, args[ARG_2], &valueType3);
    NAPI_ASSERT(env, valueType3 == napi_function, "type mismatch for parameter 3");

    //type    get value
    int32_t type;
    status = napi_get_value_int32(env, args[ARG_0], &type);
    if (status != napi_ok) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Failed to get type");
        return result;
    }

    if (type != GeofenceDataUtils::GeofenceType::GEOFENCE_OPTION_ADD_POI_GEOFENCE) {
        InvokeCallBack(env, args, false, ERROR_MESSAGE);
        return result;
    }

    //options    get value
    const char *keywordStr = "keyword";
    const char *poiTypeStr = "poiType";
    const char *poiCityStr = "poiCity";
    bool haskeyword = HasNamedProperty(env, args[ARG_1], keywordStr);
    bool haspoiType = HasNamedProperty(env, args[ARG_1], poiTypeStr);
    bool haspoiCity = HasNamedProperty(env, args[ARG_1], poiCityStr);
    NAPI_ASSERT(env, haskeyword && haspoiType && haspoiCity, "type mismatch");

    size_t len = 0;
    //Options    keyword
    napi_value keywordValue = nullptr;
    std::string keyword;
    keywordValue = GetNamedProperty(env, args[ARG_1], keywordStr);
    char keywordBuf[BUF_LEN];
    napi_get_value_string_utf8(env, keywordValue, keywordBuf, BUF_LEN, &len);
    keyword = keywordBuf;
    GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "len = %{public}d", len);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "keywordBuf = %{public}s", keywordBuf);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "keyword = %{public}s", keyword.c_str());

    //Options    poiType
    napi_value poiTypeValue = nullptr;
    std::string poiType;
    poiTypeValue = GetNamedProperty(env, args[ARG_1], poiTypeStr);
    char poiTypeBuf[BUF_LEN];
    napi_get_value_string_utf8(env, poiTypeValue, poiTypeBuf, BUF_LEN, &len);
    poiType = poiTypeBuf;
    GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "len = %{public}d", len);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "poiTypeBuf = %{public}s", poiTypeBuf);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "poiType = %{public}s", poiType.c_str());

    //Options    poiCity
    napi_value poiCityValue = nullptr;
    std::string poiCity;
    poiCityValue = GetNamedProperty(env, args[ARG_1], poiCityStr);
    char poiCityBuf[BUF_LEN];
    napi_get_value_string_utf8(env, poiCityValue, poiCityBuf, BUF_LEN, &len);
    poiCity = poiCityBuf;
    GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "len = %{public}d", len);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "poiCityBuf = %{public}s", poiCityBuf);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "poiCity = %{public}s", poiCity.c_str());

    GeofenceDataUtils::PoiOptions poiOptions;
    poiOptions.keyword = keyword;
    poiOptions.poiType = poiType;
    poiOptions.poiCity = poiCity;


    GeofenceNapi* obj = nullptr;
    bool isObjExists = false;
    for (auto it = objectMap_.begin(); it != objectMap_.end(); ++it) {
        if (it->first == type) {
            isObjExists = true;
            GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Object already exists");
            return result;
        }
    }
    if (!isObjExists) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Didn't find object, so created it");
        obj = new GeofenceNapi(env, jsthis);
        napi_wrap(env, jsthis, reinterpret_cast<void *>(obj),
            [](napi_env env, void *data, void *hint) {
                (void)env;
                (void)hint;
                GeofenceNapi *geofence = (GeofenceNapi *)data;
                delete geofence;
            },
            nullptr, &(obj->callbackRef_));
        objectMap_.insert(std::pair<int32_t, GeofenceNapi*>(type, obj));
    }

    if (!obj->On(type, args[ARG_2], false)) {
            GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "type: %{public}d already exists", type);
            return result;
    }

    sptr<IgeofenceCallback> callback;
    bool isCallbackExists = false;
    for (auto it = callbackMap_.begin(); it != callbackMap_.end(); ++it) {
        if (it->first == type) {
            isCallbackExists = true;
            break;
        }
    }
    if (!isCallbackExists) {
        callback = new GeofenceCallback();
        GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Didn't find callback, created it: %{public}p", callback.GetRefPtr());

        g_GeofenceClient.SubscribePoi(GeofenceDataUtils::GeofenceType(type), poiOptions, callback);
        callbackMap_.insert(std::pair<int32_t, sptr<IgeofenceCallback>>(type, callback));
        InvokeCallBack(env, args, false, CALLBACK_SUCCESS);
    } else {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Callback exists.");
        return result;
    }

    napi_get_undefined(env, &result);
    GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Exit");
    return result;
}

napi_value GeofenceNapi::UnSubscribePoiGeofence(napi_env env, napi_callback_info info)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Enter");
    napi_value result = nullptr;
    size_t argc = ARG_3;
    napi_value args[ARG_3] = { 0 };
    napi_value jsthis;
    void *data = nullptr;

    napi_status status = napi_get_cb_info(env, info, &argc, args, &jsthis, &data);
    NAPI_ASSERT(env, (status == napi_ok) && (argc >= 0), "Bad parameters");

    napi_valuetype valueType1 = napi_undefined;
    napi_typeof(env, args[ARG_0], &valueType1);
    GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "valueType1: %{public}d", valueType1);
    NAPI_ASSERT(env, valueType1 == napi_number, "type mismatch for parameter 1");

    napi_valuetype valueType2 = napi_undefined;
    napi_typeof(env, args[ARG_1], &valueType2);
    NAPI_ASSERT(env, valueType2 == napi_object, "type mismatch for parameter 2");

    //type    get value
    int32_t type;
    status = napi_get_value_int32(env, args[ARG_0], &type);
    if (status != napi_ok) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Failed to get type");
        return result;
    }

    if (type != GeofenceDataUtils::GeofenceType::GEOFENCE_OPTION_ADD_POI_GEOFENCE) {
        InvokeCallBack(env, args, true, ERROR_MESSAGE);
        return result;
    }

    //options    get value
    const char *keywordStr = "keyword";
    const char *poiTypeStr = "poiType";
    const char *poiCityStr = "poiCity";
    bool haskeyword = HasNamedProperty(env, args[ARG_1], keywordStr);
    bool haspoiType = HasNamedProperty(env, args[ARG_1], poiTypeStr);
    bool haspoiCity = HasNamedProperty(env, args[ARG_1], poiCityStr);
    NAPI_ASSERT(env, haskeyword && haspoiType && haspoiCity, "type mismatch");

    size_t len = 0;
    //Options    keyword
    napi_value keywordValue = nullptr;
    std::string keyword;
    keywordValue = GetNamedProperty(env, args[ARG_1], keywordStr);
    char keywordBuf[BUF_LEN];
    napi_get_value_string_utf8(env, keywordValue, keywordBuf, BUF_LEN, &len);
    keyword = keywordBuf;
    GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "len = %{public}d", len);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "keywordBuf = %{public}s", keywordBuf);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "keyword = %{public}s", keyword.c_str());

    //Options    poiType
    napi_value poiTypeValue = nullptr;
    std::string poiType;
    poiTypeValue = GetNamedProperty(env, args[ARG_1], poiTypeStr);
    char poiTypeBuf[BUF_LEN];
    napi_get_value_string_utf8(env, poiTypeValue, poiTypeBuf, BUF_LEN, &len);
    poiType = poiTypeBuf;
    GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "len = %{public}d", len);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "poiTypeBuf = %{public}s", poiTypeBuf);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "poiType = %{public}s", poiType.c_str());

    //Options    poiCity
    napi_value poiCityValue = nullptr;
    std::string poiCity;
    poiCityValue = GetNamedProperty(env, args[ARG_1], poiCityStr);
    char poiCityBuf[BUF_LEN];
    napi_get_value_string_utf8(env, poiCityValue, poiCityBuf, BUF_LEN, &len);
    poiCity = poiCityBuf;
    GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "len = %{public}d", len);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "poiCityBuf = %{public}s", poiCityBuf);
    GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "poiCity = %{public}s", poiCity.c_str());

    GeofenceDataUtils::PoiOptions poiOptions;
    poiOptions.keyword = keyword;
    poiOptions.poiType = poiType;
    poiOptions.poiCity = poiCity;

    GeofenceNapi* obj = nullptr;
    bool isObjExists = false;
    for (auto it = objectMap_.begin(); it != objectMap_.end(); ++it) {
        if (it->first == type) {
            isObjExists = true;
            obj = (GeofenceNapi*)(it->second);
            GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Found object");
        }
    }
    if (!isObjExists) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Didn't find object, so created it");
        return result;
    }

    if (!obj->Off(type, false)) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Failed to get callback for type: %{public}d", type);
        return result;
    } else {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_JS_NAPI, "erase ObjMap_");
        objectMap_.erase(type);
    }

    sptr<IgeofenceCallback> callback;
    bool isCallbackExists = false;
    for (auto it = callbackMap_.begin(); it != callbackMap_.end(); ++it) {
        if (it->first == type) {
            isCallbackExists = true;
            callback = (sptr<IgeofenceCallback>)(it->second);
            GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Found callback: %{public}p", callback.GetRefPtr());
            break;
        }
    }
    if (!isCallbackExists) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "No existed callback");
        return result;
    } else if (callback != nullptr) {
        g_GeofenceClient.UnSubscribePoi(GeofenceDataUtils::GeofenceType(type), poiOptions, callback);
        InvokeCallBack(env, args, true, CALLBACK_SUCCESS);
        callbackMap_.erase(type);
    }
    napi_get_undefined(env, &result);
    GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Exit");
    return result;
}

napi_value GeofenceNapi::SetFoolproofTactics(napi_env env, napi_callback_info info)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Enter");
    napi_value result = nullptr;
    size_t argc = ARG_2;
    napi_value args[ARG_2] = {0};
    napi_value jsthis;
    void *data = nullptr;

    napi_status status = napi_get_cb_info(env, info, &argc, args, &jsthis, &data);
    NAPI_ASSERT(env, (status == napi_ok) && (argc >= 0), "Bad parameters");

    //type
    napi_valuetype valueType1 = napi_undefined;
    napi_typeof(env, args[ARG_0], &valueType1);
    NAPI_ASSERT(env, valueType1 == napi_number, "type mismatch for parameter 1");

    //minuteTime
    napi_valuetype valueType2 = napi_undefined;
    napi_typeof(env, args[ARG_1], &valueType2);
    NAPI_ASSERT(env, valueType2 == napi_number, "type mismatch for parameter 1");

    //type    get value
    int32_t type;
    status = napi_get_value_int32(env, args[ARG_0], &type);
    if (status != napi_ok) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Failed to get type");
        return result;
    }

    //minuteTime    get value
    double minuteTime;
    status = napi_get_value_double(env, args[ARG_1], &minuteTime);
    if (status != napi_ok) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Failed to get type");
        return result;
    }

    g_GeofenceClient.SetFoolproofTactics(GeofenceDataUtils::GeofenceType(type), minuteTime);
    napi_get_undefined(env, &result);
    GEOFENCE_HILOGE(GEOFENCE_MODULE_JS_NAPI, "Exit");
    return result;

}

napi_value GeofenceNapi::EnumGeofenceTypeConstructor(napi_env env, napi_callback_info info)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Enter");
    napi_value thisArg = nullptr;
    void *data = nullptr;
    napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, &data);
    napi_value global = nullptr;
    napi_get_global(env, &global);
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Exit");
    return thisArg;
}

napi_value GeofenceNapi::CreateEnumGeofenceType(napi_env env, napi_value exports)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Enter");
    napi_value poiGeofence = nullptr;
    napi_value pointGeofence = nullptr;
    napi_value noMoreReminder = nullptr;

    napi_create_int32(env, (int32_t)GeofenceDataUtils::GeofenceType::GEOFENCE_OPTION_ADD_POI_GEOFENCE,
        &poiGeofence);
    napi_create_int32(env, (int32_t)GeofenceDataUtils::GeofenceType::GEOFENCE_OPTION_ADD_POINT_GEOFENCE,
        &pointGeofence);
    napi_create_int32(env, (int32_t)GeofenceDataUtils::GeofenceType::GEOFENCE_OPTION_NO_MORE_REMINDERS,
        &noMoreReminder);

    napi_property_descriptor desc[] = {
        DECLARE_NAPI_STATIC_PROPERTY("GEOFENCE_OPTION_ADD_POI_GEOFENCE", poiGeofence),
        DECLARE_NAPI_STATIC_PROPERTY("GEOFENCE_OPTION_ADD_POINT_GEOFENCE", pointGeofence),
        DECLARE_NAPI_STATIC_PROPERTY("GEOFENCE_OPTION_NO_MORE_REMINDERS", noMoreReminder),
    };
    napi_value result = nullptr;
    napi_define_class(env, "GeofenceType", NAPI_AUTO_LENGTH, EnumGeofenceTypeConstructor, nullptr,
        sizeof(desc) / sizeof(*desc), desc, &result);

    napi_set_named_property(env, exports, "GeofenceType", result);
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Exit");
    return exports;
}

napi_value GeofenceNapi::EnumGeofenceValueConstructor(napi_env env, napi_callback_info info)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Enter");
    napi_value thisArg = nullptr;
    void *data = nullptr;
    napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, &data);
    napi_value global = nullptr;
    napi_get_global(env, &global);
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Exit");
    return thisArg;
}

napi_value GeofenceNapi::CreateEnumGeofenceValue(napi_env env, napi_value exports)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Enter");
    napi_value outside = nullptr;
    napi_value inside = nullptr;
    napi_value enter = nullptr;
    napi_value exit = nullptr;

    napi_create_int32(env, (int32_t)GeofenceDataUtils::GeofenceValue::VALUE_OUTSIDE, &outside);
    napi_create_int32(env, (int32_t)GeofenceDataUtils::GeofenceValue::VALUE_INSIDE, &inside);
    napi_create_int32(env, (int32_t)GeofenceDataUtils::GeofenceValue::VALUE_ENTER, &enter);
    napi_create_int32(env, (int32_t)GeofenceDataUtils::GeofenceValue::VALUE_EXIT, &exit);

    napi_property_descriptor desc[] = {
        DECLARE_NAPI_STATIC_PROPERTY("VALUE_OUTSIDE", outside),
        DECLARE_NAPI_STATIC_PROPERTY("VALUE_INSIDE", inside),
        DECLARE_NAPI_STATIC_PROPERTY("VALUE_ENTER", enter),
        DECLARE_NAPI_STATIC_PROPERTY("VALUE_EXIT", exit),
    };
    napi_value result = nullptr;
    napi_define_class(env, "GeofenceValue", NAPI_AUTO_LENGTH, EnumGeofenceValueConstructor, nullptr,
        sizeof(desc) / sizeof(*desc), desc, &result);

    napi_set_named_property(env, exports, "GeofenceValue", result);
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Exit");
    return exports;
}

napi_value GeofenceNapi::ResponseConstructor(napi_env env, napi_callback_info info)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Enter");
    napi_value thisVar = nullptr;
    void *data = nullptr;
    napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);

    auto entity = new ResponseEntity();
    napi_wrap(
        env, thisVar, entity,
        [](napi_env env, void *data, void *hint) {
            GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Destructor");
            auto entity = (ResponseEntity*)data;
            delete entity;
        },
        nullptr, nullptr);
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Exit");

    return thisVar;
}

napi_value GeofenceNapi::CreateResponseClass(napi_env env, napi_value exports)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Enter");

    napi_property_descriptor desc[] = {
        DECLARE_NAPI_PROPERTY("geofenceCallbackValue", CreateGeofenceValueObject(env)),
    };

    napi_value result = nullptr;
    napi_define_class(env, "Response", NAPI_AUTO_LENGTH, ResponseConstructor, nullptr,
        sizeof(desc) / sizeof(*desc), desc, &result);

    napi_create_reference(env, result, 1, &g_responseConstructor);
    napi_set_named_property(env, exports, "Response", result);
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Exit");
    return exports;
}

napi_status GeofenceNapi::AddProperty(napi_env env, napi_value object, const std::string name, int32_t enumValue)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Enter");
    napi_status status;
    napi_value enumNapiValue;

    status = napi_create_int32(env, enumValue, &enumNapiValue);
    if (status == napi_ok) {
        status = napi_set_named_property(env, object, name.c_str(), enumNapiValue);
    }
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Exit");
    return status;
}

napi_value GeofenceNapi::CreateGeofenceValueObject(napi_env env)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Enter");
    napi_value result = nullptr;
    napi_status status;
    std::string propName;

    status = napi_create_object(env, &result);
    if (status == napi_ok) {
        for (unsigned int i = GeofenceDataUtils::GeofenceValue::VALUE_OUTSIDE; i < vecGeofenceValue.size(); i++) {
            propName = vecGeofenceValue[i];
            GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "propName: %{public}s", propName.c_str());
            status = AddProperty(env, result, propName, i);
            if (status != napi_ok) {
                GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Failed to add named prop!");
                break;
            }
            propName.clear();
        }
    }
    if (status == napi_ok) {
        // The reference count is for creation of movement value Object Reference
        status = napi_create_reference(env, result, 1, &geofenceValueRef_);
        if (status == napi_ok) {
            return result;
        }
    }
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "CreateGeofenceValueObject is Failed!");
    napi_get_undefined(env, &result);

    return result;
}

napi_value GeofenceNapi::Init(napi_env env, napi_value exports)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Enter");
    napi_property_descriptor desc[] = {
        DECLARE_NAPI_STATIC_FUNCTION("onPoint", SubscribePointGeofence),
        DECLARE_NAPI_STATIC_FUNCTION("offPoint", UnSubscribePointGeofence),
        DECLARE_NAPI_STATIC_FUNCTION("onPoi", SubscribePoiGeofence),
        DECLARE_NAPI_STATIC_FUNCTION("offPoi", UnSubscribePoiGeofence),
        DECLARE_NAPI_STATIC_FUNCTION("onSet", SetFoolproofTactics),
    };
    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));

    CreateEnumGeofenceType(env, exports);
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Exit");
    return exports;
}

EXTERN_C_START
/*
 * function for module exports
 */
static napi_value GeofenceInit(napi_env env, napi_value exports)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Enter");

    napi_value ret = GeofenceNapi::Init(env, exports);

    GEOFENCE_HILOGD(GEOFENCE_MODULE_JS_NAPI, "Exit");

    return ret;
}
EXTERN_C_END

/*
 * Module definition
 */
static napi_module g_module = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = "geofence",
    .nm_register_func = GeofenceInit,
    .nm_modname = "geofence",
    .nm_priv = ((void *)0),
    .reserved = {0}
};

/*
 * Module registration
 */
extern "C" __attribute__((constructor)) void RegisterModule(void)
{
    napi_module_register(&g_module);
}

